const PENDING = 'pending';
const REJECTED = 'rejected';
const FULFILLED = 'fulfilled';
function resolvePromise(promise, x, resolve,reject) {
  if (x === promise) {
    throw TypeError(`${x} 不能循环引用`)
    return;
  }
  if (x !== null&& typeof x === 'object' || (typeof x  === 'function')) {
    try {
      let called = false;
      const then = x.then;
      if (then && typeof then === 'function'){
        if (called) return;
        called = true;
        then.call(x, y=>{
          resolvePromise(Promise,x, resovle, reject);
        },r=>{
          if (called) return;
          called = true;
          reject(r);
        })
      }else{
       resolve(then)
      }
    }catch(e) {
      if (called) return;
      called = true;
      reject(e);
    }
  }
}
class Promise {
  constructor(exectour) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    const resolve = (value) =>  {
      if (value instanceof Promise) {
        value.then(y=>{
          this.value = y;
          this.status = FULFILLED;
        })
      } else {
        this.value =value;
        this.status = FULFILLED;
      }
      if (this.status === PENDING) { // 为了保证状态变化后 后面在执行改变状态的方法就不会执行了
        this.onResolvedCallbacks.forEach(fn=>fn);
      }
    }
    const reject = (reason) => {
      if (this.status === REJECTED) { // 为了保证状态变化后 后面在执行改变状态的方法就不会执行了
        this.onRejectedCallbacks.forEach(fn=>fn);
      }
      this.reason = reason;
      this.status = REJECTED;
    }
    try{
      exectour(resolve, reject);
    }catch(e) {
      reject(e)
    }
  }
  then(onResolved, onRejected) {
    const promise = new Promise((resolve, reject)=>{
      if (this.status === PENDING) {
        try {
          this.onResolvedCallbacks.push(()=>{
            setTimeout(()=>{
              const x = onResolved(this.value);
              resolvePromise(promise, x, resovle, reject);
            },0)
          });
          this.onRejectedCallbacks.push(()=>{
            setTimeout(()=>{
              const x = onRejected(this.resovle);
              resolvePromise(promise, x, resovle, reject);
            },0)
          });
        } catch(e) {
          reject(e);
        }
      }
      if (this.status === REJECTED) {
        setTimeout(()=>{
          const x = onRejected(this.reason);
          resolvePromise(promise, x, resovle, reject);
        },0)
      }
      if (this.status === FULFILLED){
        setTimeout(()=>{
          const x = onResolved(this.value);
          resolvePromise(promise, x, resovle, reject);
        },0)
      }
    });
    return promise;
  }
  finally(cb) {
    return this.then(
      y=> Promise.resolve(cb()).then(()=>y),
      r=>Promise.resolve(cb()).then(()=>{throw new Error(r)}))
  }
  catch(fn) {
    return this.then(null, err=>{
      return Promise.resolve(fn).then(()=>{throw new Error(err)})
    })
  }
  static resolve(value) {
    return new Promise((res,rej)=>{
      res(value)
    })
  }
  static reject(err) {
    return new Promise((res,rej)=>{
      rej(err)
    })
  }
  static all(arr) {
    return new Promise((resolve, reject)=>{
      const res = [];
      const j= 0;
      function process(data, key) {
        res[key] = data;
        if (++j === arr.length) {
          resolve(res)
        }
      }
      for (let i = 0; i < arr.length;  i++) {
        const item = arr[i];
        try {
          const x = item.then;
          if (x && typeof x === 'function') {
            item.then(y=>{
              process(y,i)
            },reject)
          }else {
            process(item, i);
          }
        }catch(e) {
          reject(e)
        }
      }
    })
  }
  static race(arr) {
    return new Promise((resolve,reject)=>{
      for (let i = 0; i< arr.length; i++) {
        const item = arr[i];
        try{
          const x = item.then;
          if(x && typeof x === 'function') {
            item.then(y=>{
              resolve(y);
            }, r=>{
              reject(r);
            })
          }else {
            resolve(item);
          }
        }catch(e) {
          reject(e);
        }
      }
    })
  }
  static any() {
  }
  static allSettled() {

  }
}